#include "NPC.h"
#include "NPCGroup.h"
#include "Terrain.h"
#include <cmath>

NPC::NPC()
    : m_pMesh(NULL)
    , m_pTexture(NULL)
    , m_currentState(NPC_STATE_IDLE)
    , m_stateTime(0.0f)
    , m_health(100.0f)
    , m_maxHealth(100.0f)
    , m_attackDamage(10.0f)
    , m_attackCooldown(2.0f)
    , m_currentCooldown(0.0f)
    , m_pTarget(NULL)
    , m_pGroup(NULL)
    , m_pTerrain(NULL)
    , m_pObstacles(NULL)
    , m_spawnPosition(0, 0, 0)
    , m_patrolPoint(0, 0, 0)
    , m_speed(15.0f)
    , m_viewRange(80.0f)      // 缩小视野范围从 150 -> 80，避免一出生就发现玩家
    , m_attackRange(20.0f)    // 缩小攻击范围从 50 -> 20，更合理的近战距离
    , m_retreatHealthThreshold(30.0f)
{
}

NPC::~NPC()
{
    Cleanup();
}

bool NPC::Initialize(LPDIRECT3DDEVICE9 device, const D3DXVECTOR3& spawnPos)
{
    m_spawnPosition = spawnPos;
    SetPosition(spawnPos);

    // Create NPC mesh (增大尺寸以便远距离识别)
    HRESULT hr = D3DXCreateBox(device, 12.0f, 15.0f, 12.0f, &m_pMesh, NULL);
    if (FAILED(hr))
        return false;

    // Set bounding box (local space, relative to mesh center)
    m_boundingBox = AABB(
        D3DXVECTOR3(-6.0f, -7.5f, -6.0f),
        D3DXVECTOR3(6.0f, 7.5f, 6.0f)
    );

    // Create texture
    hr = D3DXCreateTexture(device, 64, 64, 1, 0, D3DFMT_A8R8G8B8,
        D3DPOOL_MANAGED, &m_pTexture);
    if (SUCCEEDED(hr))
    {
        D3DLOCKED_RECT rect;
        m_pTexture->LockRect(0, &rect, NULL, 0);
        DWORD* pTexels = (DWORD*)rect.pBits;

        // Minecraft 风格的红色装甲纹理（敌方坦克）
        for (int y = 0; y < 64; y++)
        {
            for (int x = 0; x < 64; x++)
            {
                int i = y * 64 + x;

                // 基础颜色：深红色（敌方识别色）
                int r = 200;
                int g = 50;
                int b = 50;

                // 8x8 像素网格（Minecraft 风格，比其他物体更小的网格）
                bool isBorder = (x % 8 == 0 || y % 8 == 0);
                if (isBorder)
                {
                    // 边框深色
                    r -= 40;
                    g -= 20;
                    b -= 20;
                }

                // 添加金属光泽效果
                if ((x / 4) % 2 == 0 && (y / 4) % 2 == 0)
                {
                    r += 30;
                    g += 10;
                    b += 10;
                }

                // 添加随机噪点
                int noise = (rand() % 20) - 10;
                r += noise;
                g += noise / 2;
                b += noise / 2;

                // 确保颜色范围有效
                if (r < 140) r = 140; if (r > 255) r = 255;
                if (g < 30) g = 30; if (g > 100) g = 100;
                if (b < 30) b = 30; if (b > 100) b = 100;

                pTexels[i] = D3DCOLOR_ARGB(255, r, g, b);
            }
        }
        m_pTexture->UnlockRect(0);
    }

    // Set initial patrol point
    m_patrolPoint = spawnPos + D3DXVECTOR3(50.0f, 0.0f, 50.0f);

    SetState(NPC_STATE_PATROL);

    return true;
}

void NPC::Update(float deltaTime)
{
    if (IsDead())
        return;

    m_stateTime += deltaTime;
    m_currentCooldown -= deltaTime;
    if (m_currentCooldown < 0.0f)
        m_currentCooldown = 0.0f;

    UpdateState(deltaTime);
}

void NPC::UpdateState(float deltaTime)
{
    switch (m_currentState)
    {
    case NPC_STATE_IDLE:
        UpdateIdle(deltaTime);
        break;
    case NPC_STATE_PATROL:
        UpdatePatrol(deltaTime);
        break;
    case NPC_STATE_CHASE:
        UpdateChase(deltaTime);
        break;
    case NPC_STATE_ATTACK:
        UpdateAttack(deltaTime);
        break;
    case NPC_STATE_RETREAT:
        UpdateRetreat(deltaTime);
        break;
    case NPC_STATE_DEAD:
        break;
    }
}

void NPC::UpdateIdle(float deltaTime)
{
    // After 2 seconds of idle, start patrolling
    if (m_stateTime > 2.0f)
    {
        SetState(NPC_STATE_PATROL);
    }
}

void NPC::UpdatePatrol(float deltaTime)
{
    // Move towards patrol point
    MoveTowards(m_patrolPoint, deltaTime);

    // Check if reached patrol point
    D3DXVECTOR3 diff = m_patrolPoint - m_position;
    float dist = D3DXVec3Length(&diff);

    if (dist < 5.0f)
    {
        // Pick new patrol point
        float angle = (float)(rand() % 360) * D3DX_PI / 180.0f;
        float radius = 30.0f + (rand() % 50);
        m_patrolPoint = m_spawnPosition + D3DXVECTOR3(
            cos(angle) * radius,
            0.0f,
            sin(angle) * radius
        );
    }

    // Check for target in view range
    if (m_pTarget && DistanceToTarget() < m_viewRange)
    {
        SetState(NPC_STATE_CHASE);
    }
}

void NPC::UpdateChase(float deltaTime)
{
    if (!m_pTarget)
    {
        SetState(NPC_STATE_PATROL);
        return;
    }

    float dist = DistanceToTarget();

    // Lost target
    if (dist > m_viewRange * 1.5f)
    {
        SetState(NPC_STATE_PATROL);
        return;
    }

    // Check if low health
    if (m_health < m_retreatHealthThreshold)
    {
        SetState(NPC_STATE_RETREAT);
        return;
    }

    // In attack range
    if (dist < m_attackRange)
    {
        SetState(NPC_STATE_ATTACK);
        return;
    }

    // Chase target
    MoveTowards(m_pTarget->GetPosition(), deltaTime);
}

void NPC::UpdateAttack(float deltaTime)
{
    if (!m_pTarget)
    {
        SetState(NPC_STATE_PATROL);
        return;
    }

    float dist = DistanceToTarget();

    // Target moved away
    if (dist > m_attackRange * 1.2f)
    {
        SetState(NPC_STATE_CHASE);
        return;
    }

    // Check if low health
    if (m_health < m_retreatHealthThreshold)
    {
        SetState(NPC_STATE_RETREAT);
        return;
    }

    // Face target
    D3DXVECTOR3 direction = m_pTarget->GetPosition() - m_position;
    float angle = atan2(direction.z, direction.x);
    m_rotation.y = angle;

    // Attack if cooldown ready
    if (m_currentCooldown <= 0.0f)
    {
        Attack(m_pTarget);
        m_currentCooldown = m_attackCooldown;
    }
}

void NPC::UpdateRetreat(float deltaTime)
{
    // Move away from target towards spawn
    MoveTowards(m_spawnPosition, deltaTime);

    // If health recovered or reached spawn, go back to patrol
    if (m_health > m_retreatHealthThreshold * 1.5f ||
        D3DXVec3Length(&(m_position - m_spawnPosition)) < 10.0f)
    {
        SetState(NPC_STATE_PATROL);
    }
}

void NPC::SetState(NPCState newState)
{
    if (m_currentState != newState)
    {
        m_currentState = newState;
        m_stateTime = 0.0f;
    }
}

void NPC::TakeDamage(float damage)
{
    m_health -= damage;
    if (m_health < 0.0f)
        m_health = 0.0f;

    if (IsDead())
    {
        SetState(NPC_STATE_DEAD);
    }
}

void NPC::Attack(GameObject* target)
{
    // Simple attack - just apply damage
    // In a real game, you would check line of sight, etc.
}

void NPC::MoveTowards(const D3DXVECTOR3& target, float deltaTime)
{
    D3DXVECTOR3 direction = target - m_position;
    direction.y = 0.0f;  // Keep on ground

    float length = D3DXVec3Length(&direction);
    if (length > 0.1f)
    {
        D3DXVec3Normalize(&direction, &direction);

        D3DXVECTOR3 newPos = m_position + direction * m_speed * deltaTime;

        // Update Y coordinate based on terrain height
        if (m_pTerrain)
        {
            newPos.y = m_pTerrain->GetHeight(newPos.x, newPos.z) + 7.5f;  // 7.5f is half the NPC height (15.0f box)
        }

        // Check map boundaries (0-1000 range)
        if (newPos.x < 0.0f || newPos.x > 1000.0f || newPos.z < 0.0f || newPos.z > 1000.0f)
        {
            return;  // Don't move outside the map
        }

        // Check collision with obstacles
        bool collision = false;
        if (m_pObstacles)
        {
            // Create temporary GameObject to test collision at new position
            GameObject tempObj;
            tempObj.SetPosition(newPos);
            tempObj.SetBoundingBox(m_boundingBox);  // Use local space bounding box

            for (size_t i = 0; i < m_pObstacles->size(); i++)
            {
                if (tempObj.CheckCollision(*(*m_pObstacles)[i]))
                {
                    collision = true;
                    break;
                }
            }
        }

        if (!collision)
        {
            SetPosition(newPos);

            // Face movement direction
            m_rotation.y = atan2(direction.z, direction.x);
        }
    }
}

float NPC::DistanceToTarget() const
{
    if (!m_pTarget)
        return FLT_MAX;

    D3DXVECTOR3 diff = m_pTarget->GetPosition() - m_position;
    return D3DXVec3Length(&diff);
}

void NPC::Render(LPDIRECT3DDEVICE9 device)
{
    if (!device || !m_pMesh || IsDead())
        return;

    // Set world matrix
    D3DXMATRIX matWorld, matScale, matRot, matTrans;
    D3DXMatrixScaling(&matScale, 1.0f, 1.0f, 1.0f);

    D3DXMATRIX matRotX, matRotY, matRotZ;
    D3DXMatrixRotationX(&matRotX, m_rotation.x);
    D3DXMatrixRotationY(&matRotY, m_rotation.y);
    D3DXMatrixRotationZ(&matRotZ, m_rotation.z);
    matRot = matRotX * matRotY * matRotZ;

    D3DXMatrixTranslation(&matTrans, m_position.x, m_position.y, m_position.z);

    matWorld = matScale * matRot * matTrans;
    device->SetTransform(D3DTS_WORLD, &matWorld);

    // Set material
    D3DMATERIAL9 material = {};
    material.Diffuse.r = material.Ambient.r = 1.0f;
    material.Diffuse.g = material.Ambient.g = 0.3f;
    material.Diffuse.b = material.Ambient.b = 0.3f;
    material.Diffuse.a = material.Ambient.a = 1.0f;
    device->SetMaterial(&material);

    // Set texture
    if (m_pTexture)
    {
        device->SetTexture(0, m_pTexture);
    }

    // Render mesh
    m_pMesh->DrawSubset(0);
}

void NPC::Cleanup()
{
    if (m_pTexture)
    {
        m_pTexture->Release();
        m_pTexture = NULL;
    }
    if (m_pMesh)
    {
        m_pMesh->Release();
        m_pMesh = NULL;
    }
}
